package aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;

import javax.sql.DataSource;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import po.UserIndex;
import service.UserService;
import annotation.DBUrl;

@Aspect
public class DataSourceAspect implements ApplicationContextAware{
	
	private ApplicationContext applicationContext;
	
	private static HashMap<String, DataSource> map = new HashMap<String, DataSource>();
	
	@Before("execution(* dao.user.impl.*.*(..))")
	public void processDataSource(JoinPoint jp) throws Throwable {
		//Tool.dumpInfo(jp);
		
		Object[] args = jp.getArgs();
		if( args.length <=0 ) return;
		
		String dbUrl = null;
		
		//find @DBUrl first
		Class<?>[] parameterTypes = new Class[args.length];
		for(int i=0;i<args.length;i++) {
			parameterTypes[i] = args[i].getClass();
		}
		
		Method method = jp.getTarget().getClass().getMethod(jp.getSignature().getName(), parameterTypes);
		
		Annotation[][] annotations = method.getParameterAnnotations();
		
		for(int i=0;i<args.length;i++) {
			Annotation[] argAnnotations = annotations[i];
			for(Annotation annotation : argAnnotations){
				if(annotation instanceof  DBUrl){
					dbUrl = (String)args[i];
					break;
				}
			}
			if( dbUrl != null) break;
		}

		//find loginname from the first parameter
		if( dbUrl == null ){
			Object obj = args[0];
			
			String loginname = "";
			if( obj instanceof String) {
				loginname = (String)obj;
			}
			else {
				try{
					Method method1 = obj.getClass().getMethod("getLoginname");
					loginname = (String)method1.invoke(obj);
				}catch(Exception e){
					//TODO ERROR
					return;
				}
				//TODO ERROR
				return;
			}
			
			UserService userService = (UserService)applicationContext.getBean("userService");
			UserIndex userIndex = userService.getUserIndex(loginname);
			if(userIndex == null){
				//TODO ERROR
				return;
			}
			dbUrl = userIndex.getDburl();
		}
		
		SqlMapClientDaoSupport sqlMapClientDaoSupport = (SqlMapClientDaoSupport)jp.getTarget();
		SimpleDriverDataSource basicDataSource = (SimpleDriverDataSource)sqlMapClientDaoSupport.getDataSource();
		
		if( dbUrl.equalsIgnoreCase(basicDataSource.getUrl())) {
			return;
		}
		
		DataSource dataSource = this.getDataSource(dbUrl);
		sqlMapClientDaoSupport.setDataSource(dataSource);
		sqlMapClientDaoSupport.afterPropertiesSet();
		
	}
	
	//should be syncronized
	private synchronized DataSource getDataSource(String url) {
		DataSource dataSource = (DataSource)map.get(url);
		if( dataSource == null ){
			SimpleDriverDataSource basicDataSource= (SimpleDriverDataSource)applicationContext.getBean("dataSource");
			basicDataSource.setUrl(url);
			dataSource = (DataSource)basicDataSource;
			map.put(url, dataSource);
		}
		return dataSource;
	}

	@Override
	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		applicationContext = context;
	}
}
